//成功是返回1，还是0？
//ucos是返回1， Linux是返回0.
//需要确认
#include "yaffs_nand.h"
#include "goldfish_Chunk_API.h"
#include "goldfish_nand_reg.h"  
#include <asm/div64.h>
#include "yaffs_trace.h"
typedef enum {
	OOB_PLACE,
	OOB_AUTO,
	OOB_RAW,
} oob_mode_t;

struct oob_ops {
	oob_mode_t	mode;
	size_t		len;
	size_t		retlen;
	size_t		ooblen;
	size_t		oobretlen;
	uint32_t	ooboffs;
	uint8_t		*datbuf;
	uint8_t		*oobbuf;
};


typedef unsigned long long nand_addr;

extern u_int32_t base_nand[];
//extern goldfish_nand_cmd;

static unsigned int goldfish_nand_cmd(struct yaffs_DeviceStruct * dev, enum nand_cmd cmd, loff_t addr, unsigned int len, void *ptr)
{
        unsigned int rv; 
        unsigned char  *base; //= dev->base;//设备的基地址，这个是重点
	int id;
//	printk("aaaaaaa\n");
//	if(dev->param.name == "system")
	if(!strcmp(dev->param.name,"system"))
		id=0;
	else
	{
		if(!strcmp(dev->param.name,"userdata"))
			id=1;
		else 
		{
			if(!strcmp(dev->param.name,"cache"))
				id=2;
			else
				printk("ERROR COMMAND \n");
		}
	}
	base = base_nand[id];
//	printk("cmd:%d,base:%x\n",cmd,base);
	__raw_writel(id, base + NAND_DEV);//向哪一个设备写
        __raw_writel((uint32_t)(addr >> 32), base + NAND_ADDR_HIGH);
        __raw_writel((uint32_t)addr, base + NAND_ADDR_LOW);
        __raw_writel(len, base + NAND_TRANSFER_SIZE);
        __raw_writel((__u32)ptr, base + NAND_DATA);
        __raw_writel(cmd, base + NAND_COMMAND);
        rv = __raw_readl(base + NAND_RESULT);

        return rv; 
}
static int goldfish_nand_read(struct yaffs_DeviceStruct *dev, loff_t from, size_t len, size_t *retlen, __u8 *buf)
{
	uint32_t rem;

//	if(from + len > dev->)
//		goto invalid_arg;
//	if(len != mtd->writesize)
//		goto invalid_arg;

	rem = do_div(from, dev->nDataBytesPerChunk);
	from *= (dev->nDataBytesPerChunk + dev->param.spareBytesPerChunk);
	printk("%s,from:%x\n", __func__, from);
	*retlen = goldfish_nand_cmd(dev, NAND_CMD_READ, from, len, buf);
	return 0;

//invalid_arg:
//	printk("goldfish_nand_read: invalid read, start %llx, len %x, dev_size %x"
//	       ", write_size %x\n", from, len, mtd->size, mtd->writesize);
//	return -EINVAL;
}
static int goldfish_nand_write_oob(struct yaffs_DeviceStruct *dev, loff_t to, struct oob_ops *ops)
{
	uint32_t rem;
	
	rem = do_div(to, dev->nDataBytesPerChunk);
	if(rem)
		YBUG();
	to *= (dev->nDataBytesPerChunk + dev->param.spareBytesPerChunk);

	if(ops->datbuf)
	{
//		printk("data %s,%x\n", __func__, to);
		ops->retlen = goldfish_nand_cmd(dev, NAND_CMD_WRITE, to,
		                            ops->len, ops->datbuf);
//		printk("ret:%s,%d\n", __func__, ops->retlen);
	}
	to += dev->nDataBytesPerChunk + ops->ooboffs;
	if(ops->oobbuf)
	{
//		printk("oob %s,%x\n",__func__, to);
		ops->oobretlen = goldfish_nand_cmd(dev, NAND_CMD_WRITE, to,
		                               ops->ooblen, ops->oobbuf);
//		printk("ret %s,%d\n",__func__, ops->oobretlen);
	}
	return 0;
}

static int goldfish_nand_read_oob(struct yaffs_DeviceStruct *dev, loff_t from, struct oob_ops *ops)
{
	uint32_t rem;
//	printk("from:%llx\n", from);
//	if(ofs + ops->len > mtd->size)
//		goto invalid_arg;
//	if(ops->datbuf && ops->len && ops->len != mtd->writesize)
//		goto invalid_arg;
//	if(ops->ooblen + ops->ooboffs > mtd->oobsize)
//		goto invalid_arg;

	rem = do_div(from, dev->nDataBytesPerChunk);
	from *= (dev->nDataBytesPerChunk + dev->param.spareBytesPerChunk);

	if(ops->datbuf)
	{
//		printk("data %s,%x\n", __func__, from);
		ops->retlen = goldfish_nand_cmd(dev, NAND_CMD_READ, from, ops->len, ops->datbuf);
//		printk("ret:%s,%d\n", __func__, ops->retlen);
	}

	from += dev->nDataBytesPerChunk + ops->ooboffs;
//      printk("from:%llx\n",from);
	if(ops->oobbuf)
	{
//		printk("oob %s,%x\n",__func__, from);
		ops->oobretlen = goldfish_nand_cmd(dev, NAND_CMD_READ, from, ops->ooblen, ops->oobbuf);
//		printk("ret %s,%d\n",__func__, ops->oobretlen);
	}
//	printk("ops->retlen:%d\t ops->oobretlen:%d\n", ops->retlen, ops->oobretlen);
	return 0;
}

static int goldfish_nand_block_isbad(struct yaffs_DeviceStruct *dev, loff_t ofs)
{
	uint32_t rem;

	rem = do_div(ofs, (dev->nDataBytesPerChunk * dev->param.nChunksPerBlock));//得到block
	if(rem)
		YBUG();
	ofs *= dev->param.nChunksPerBlock;//得到chunk
	ofs *= (dev->nDataBytesPerChunk + dev->param.spareBytesPerChunk);//总的
//	printk("%s %x\n", __func__, ofs);
	return goldfish_nand_cmd(dev, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);

}

static int goldfish_nand_block_markbad(struct yaffs_DeviceStruct *dev, loff_t ofs)
{
	uint32_t rem;

	rem = do_div(ofs, dev->nDataBytesPerChunk * dev->param.nChunksPerBlock);
	if(rem)
		YBUG();
	ofs *= dev->param.nChunksPerBlock;
	ofs *= (dev->nDataBytesPerChunk + dev->param.spareBytesPerChunk);

//	printk("%s %x", __func__, ofs);
	if(goldfish_nand_cmd(dev, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
		return -5;
	return 0;

}
int Goldfish_Chunk_ReadChunk(struct yaffs_DeviceStruct * dev, int chunk_index,  __u8 * data, yaffs_ExtendedTags * tags)
{
	size_t * dummy;
	int len;//一个chunk的长度
	loff_t from;//
	int retval;
	struct oob_ops ops;
	yaffs_PackedTags2 pt;
	__u8 * spareBuffer;
	
	from = chunk_index * (dev->nDataBytesPerChunk);//chunk_index * (chunk_length + oob_size)
	
	if(dev->param.inbandTags)
		printk("Error configuration for yaffs2\n ");
	if(dev->param.inbandTags || (data && !tags))//若只读data的话
		retval = goldfish_nand_read(dev, from, dev->param.totalBytesPerChunk, &dummy, data);
	else if(tags)//如果同时读取data和tags，或者只读取tags的话
	{
		 spareBuffer = YMALLOC(dev->param.spareBytesPerChunk);
		 ops.mode = OOB_AUTO;
		 ops.ooblen = sizeof(pt);
		 ops.len = data ? dev->nDataBytesPerChunk : sizeof(pt);
		 ops.ooboffs = 0;
		 ops.datbuf = data;
		 ops.oobbuf = spareBuffer;
//		 printk("mtd->read_oob:%x\n", mtd->read_oob);
		 retval = goldfish_nand_read_oob(dev, from, &ops);
	}

	if(dev->param.inbandTags)
		printk("Error configuration for yaffs2\n ");
	else if(tags)
	{
		memcpy(&pt, spareBuffer, sizeof(pt));
		yaffs_UnpackTags2(tags, &pt, !dev->param.noTagsECC);
		YFREE(spareBuffer);
	}
	
	if( 0==retval )
		return 1;//YAFFS_OK
	else
		return 0;//YAFFS_FAIL
}
int Goldfish_Chunk_EreaseBlock(struct yaffs_DeviceStruct * dev, int block_index)
{
	//
	int len;//一个chunk的长度
	loff_t from;//
	int ret;

	from = block_index * dev->param.nChunksPerBlock;//chunk #
	from *= (dev->nDataBytesPerChunk + dev->param.spareBytesPerChunk);//total #
	len = (dev->nDataBytesPerChunk + dev->param.spareBytesPerChunk ) * dev->param.nChunksPerBlock;
	ret = goldfish_nand_cmd(dev, NAND_CMD_ERASE, from, len, NULL);
	if(ret != len)
	{
		printk("ERASE FAILED ON %d\n",block_index);
		return -1;
	}
	return 1;//right ,and back 1;
}
int Goldfish_Chunk_Initialize(struct yaffs_DeviceStruct * dev)
{
	return YAFFS_OK;
}
int Goldfish_Chunk_WriteChunk(struct yaffs_DeviceStruct * dev, int chunk_index, const __u8 * data, const yaffs_ExtendedTags * tags)
{
	int retval;
	loff_t to;
	struct oob_ops ops;
	yaffs_PackedTags2 pt;

	to = chunk_index * (dev->param.totalBytesPerChunk);

	if(!data && !tags)
	{
		printk("ERROR WRITE--NO DATA AND NO tags \n");
		YBUG();
	}
	else if(dev->param.inbandTags)
	{
		printk("Config error\n");
		YBUG();
	}
	else
		yaffs_PackTags2(&pt, tags, !dev->param.noTagsECC);

	ops.mode = OOB_AUTO;
	ops.ooblen = (dev->param.inbandTags) ? 0 : sizeof(pt);
	ops.len = dev->param.totalBytesPerChunk;
	ops.ooboffs = 0;
	ops.datbuf = (__u8 *)data;
	ops.oobbuf = (dev->param.inbandTags)? NULL : (void *)&pt;

	retval = goldfish_nand_write_oob(dev, to, &ops);

}
int Goldfish_Chunk_MarkBadBlock(struct yaffs_DeviceStruct * dev, int blockNo)
{
	int retval;

	retval = goldfish_nand_block_markbad(dev, blockNo * dev->param.nChunksPerBlock * dev->nDataBytesPerChunk);

	return !retval;
//	if (retval == 0)
//		return 1;
//	else
//		return YAFFS_FAIL;

}
int Goldfish_Chunk_QueryBlock(struct yaffs_DeviceStruct * dev, int blockNo, yaffs_BlockState * state, __u32 * sequenceNumber)
{

	int retval;

	retval = goldfish_nand_block_isbad(dev, blockNo * dev->param.nChunksPerBlock * dev->nDataBytesPerChunk);

	if (retval) {
		T(1, (TSTR("block is bad" TENDSTR)));

		*state = YAFFS_BLOCK_STATE_DEAD;
		*sequenceNumber = 0;
	} else {
		yaffs_ExtendedTags t;
		Goldfish_Chunk_ReadChunk(dev, blockNo *dev->param.nChunksPerBlock, NULL,  &t);
		if (t.chunkUsed) {
			*sequenceNumber = t.sequenceNumber;
			*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
		} else {
			*sequenceNumber = 0;
			*state = YAFFS_BLOCK_STATE_EMPTY;
		}
	}
//	T(YAFFS_TRACE_MTD,
//	  (TSTR("block is bad seq %d state %d" TENDSTR), *sequenceNumber,
//	   *state));

	if (retval == 0)
		return YAFFS_OK;
	else
		return YAFFS_FAIL;

}

